import java.io.*;
import java.net.*;
import java.util.*;

public class UDPClient {
    private static final int TIMEOUT = 100; // 超时时间100ms
    private static final int MAX_TRIES = 2; // 最大重传次数
    private static final String VERSION = "2"; // 版本号
    private static final int PORT = 9876; // 服务器端口

    public static void main(String[] args) throws IOException {
        if (args.length != 2) {
            System.out.println("Usage: java UDPClient <Server IP> <Server Port>");
            return;
        }

        String serverIP = args[0];
        int serverPort = Integer.parseInt(args[1]);

        // 创建UDP socket
        DatagramSocket socket = new DatagramSocket();
        socket.setSoTimeout(TIMEOUT);

        // 模拟TCP连接建立过程
        if (!establishConnection(socket, serverIP, serverPort)) {
            System.out.println("Failed to establish connection.");
            return;
        }

        int packetsReceived = 0; // 接收到的报文数
        int packetsSent = 12; // 发送的报文数
        int packetsTotalSent = 0;
        List<Long> rtts = new ArrayList<>(); // RTT列表
        long firstResponseTime = 0; // 第一个响应时间
        long lastResponseTime = 0; // 最后一个响应时间

        // 发送12个请求报文
        for (int i = 1; i <= packetsSent; i++) {
            String message = "Seq:" + (i - 1) * 2 + ",Ver:" + VERSION + ",Data:Hello"; // 自定义报文
            byte[] buffer = message.getBytes();
            DatagramPacket requestPacket = new DatagramPacket(buffer, buffer.length, InetAddress.getByName(serverIP), serverPort);

            boolean receivedResponse = false; // 是否收到响应
            int tries = 0; // 重传次数
            long rtt = 0; // RTT

            // 发送报文并等待响应
            while (!receivedResponse && tries < MAX_TRIES) {
                packetsTotalSent++;
                socket.send(requestPacket); // 发送报文
                long startTime = System.currentTimeMillis(); // 记录发送时间

                try {
                    DatagramPacket responsePacket = new DatagramPacket(new byte[1024], 1024); // 创建接收报文
                    socket.receive(responsePacket); // 接收报文
                    long endTime = System.currentTimeMillis(); // 记录接收时间
                    rtt = endTime - startTime; // 计算RTT

                    String response = new String(responsePacket.getData(), 0, responsePacket.getLength());
                    System.out.println("Received from server: " + response);

                    if (response.startsWith("Seq:" + i)) {
                        packetsReceived++; // 增加接收报文数
                        rtts.add(rtt); // 记录RTT
                        if (packetsReceived == 1) {
                            firstResponseTime = endTime; // 记录第一个响应时间
                        }
                        lastResponseTime = endTime; // 记录最后一个响应时间
                        System.out.println("Seq:" + i + ", Server:" + responsePacket.getAddress() + ":" + responsePacket.getPort() + ", RTT:" + rtt + "ms");
                        receivedResponse = true; // 标记为收到响应
                    }

                } catch (SocketTimeoutException e) {
                    tries++;
                    System.out.println("Seq:" + i + ", request time out, retry " + tries);
                }
            }

            if (!receivedResponse) {
                System.out.println("Seq:" + i + ", request time out");
            }
        }

        // 模拟TCP连接释放过程
        releaseConnection(socket, serverIP, serverPort);

        socket.close(); // 关闭socket
        printSummary(packetsTotalSent, packetsReceived, rtts, firstResponseTime, lastResponseTime); // 打印汇总信息
    }

    // 模拟TCP连接建立过程
    private static boolean establishConnection(DatagramSocket socket, String serverIP, int serverPort) throws IOException {
        String synMessage = "SYN"; // SYN报文
        byte[] synBuffer = synMessage.getBytes();
        DatagramPacket synPacket = new DatagramPacket(synBuffer, synBuffer.length, InetAddress.getByName(serverIP), serverPort);

        int tries = 0;
        while (tries < MAX_TRIES) {
            socket.send(synPacket); // 发送SYN报文
            System.out.println("Sent: SYN");

            try {
                DatagramPacket responsePacket = new DatagramPacket(new byte[1024], 1024); // 创建接收报文
                socket.receive(responsePacket); // 接收报文
                String response = new String(responsePacket.getData(), 0, responsePacket.getLength());
                if ("SYN-ACK".equals(response)) {
                    System.out.println("Received: SYN-ACK");
                    String ackMessage = "ACK"; // ACK报文
                    byte[] ackBuffer = ackMessage.getBytes();
                    DatagramPacket ackPacket = new DatagramPacket(ackBuffer, ackBuffer.length, InetAddress.getByName(serverIP), serverPort);
                    socket.send(ackPacket); // 发送ACK报文
                    System.out.println("Sent: ACK");
                    return true; // 连接建立成功
                }
            } catch (SocketTimeoutException e) {
                tries++;
                System.out.println("SYN-ACK timeout, retry " + tries);
            }
        }
        return false; // 连接建立失败
    }

    // 模拟TCP连接释放过程
    private static void releaseConnection(DatagramSocket socket, String serverIP, int serverPort) throws IOException {
        String finMessage = "FIN"; // FIN报文
        byte[] finBuffer = finMessage.getBytes();
        DatagramPacket finPacket = new DatagramPacket(finBuffer, finBuffer.length, InetAddress.getByName(serverIP), serverPort);

        int tries = 0;
        while (tries < MAX_TRIES) {
            socket.send(finPacket); // 发送FIN报文
            System.out.println("Sent: FIN");

            try {
                DatagramPacket responsePacket = new DatagramPacket(new byte[1024], 1024); // 创建接收报文
                socket.receive(responsePacket); // 接收报文
                String response = new String(responsePacket.getData(), 0, responsePacket.getLength());
                if ("ACK".equals(response)) {
                    System.out.println("Received: ACK");
                    return; // 连接释放成功
                }
            } catch (SocketTimeoutException e) {
                tries++;
                System.out.println("ACK timeout, retry " + tries);
            }
        }
    }

    // 打印汇总信息
    private static void printSummary(int packetsSent, int packetsReceived, List<Long> rtts, long firstResponseTime, long lastResponseTime) {
        double lossRate = (1 - ((double) packetsReceived / packetsSent)) * 100;
        long maxRTT = Collections.max(rtts);
        long minRTT = Collections.min(rtts);
        double avgRTT = rtts.stream().mapToLong(Long::longValue).average().orElse(0.0);
        double stdDevRTT = Math.sqrt(rtts.stream().mapToDouble(r -> Math.pow(r - avgRTT, 2)).average().orElse(0.0));

        System.out.println("Summary:");
        System.out.println("Packets sent: " + packetsSent);
        System.out.println("Packets received: " + packetsReceived);
        System.out.println("Loss rate: " + lossRate + "%");
        System.out.println("Max RTT: " + maxRTT + "ms");
        System.out.println("Min RTT: " + minRTT + "ms");
        System.out.println("Avg RTT: " + avgRTT + "ms");
        System.out.println("RTT Std Dev: " + stdDevRTT + "ms");
        System.out.println("Server response time span: " + (lastResponseTime - firstResponseTime) + "ms");
    }
}
